using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using cframework;
using System.ComponentModel;
using Newtonsoft.Json;
using static UnityEngine.EventSystems.EventTrigger;
using System.Reflection;
using System.Linq;
using System;
using static cframework.BuildPlayerSetting;
using HybridCLR.Editor.Commands;
using UnityEditor.Build;

namespace cframework
{
    [EssentialTools("git", "https://git-scm.com/downloads")]
    [CreateAssetMenu(menuName = "cframework/BuildPlayerSetting")]
    public class BuildPlayerSetting : ScriptableObject
    {
        public class PublishInfo
        {
            public string projectPath;
            public string frameworkDataPath;
            public string outputAssetbundleDir;
            public string buildPath;
            public string outputProjectPath;
            public string buildOutputPath;
            public string appName;
            public int hotupdateVersionNumber;
            public ulong timestamp;
            public string middleDir;
            public string branchName;
            public string realAppVersion;
            public string lastChangedVersion;
        }

        [Essential(Platform.unknown)]
        public Platform platform;
        public SubPlatform subPlatform;
        [Essential]
        public string publishType;
        [Essential]
        public string appVersion;
        [Essential]
        public int appVersionNumber;

        [Essential]
        public string packageName;



        public enum NativeBuildType
        {
            None,
            Release,
            Debug,
        }

        public bool developmentBuild;
        public NativeBuildType nativeBuildType = NativeBuildType.None;

        public ScriptingImplementation scriptingImplemention = ScriptingImplementation.IL2CPP;
        public ApiCompatibilityLevel apiCompatibilityLevel = ApiCompatibilityLevel.NET_Unity_4_8;
        public Il2CppCompilerConfiguration cppCompiler = Il2CppCompilerConfiguration.Release;
        public UnityEditor.Build.Il2CppCodeGeneration il2CppCodeGeneration = UnityEditor.Build.Il2CppCodeGeneration.OptimizeSpeed;
        public ManagedStrippingLevel managedStrippingLevel = ManagedStrippingLevel.Minimal;


        public bool enableHotUpdate = true;
        public bool enableDownload = true;
        public int minAppVersion = 0;
        public int inAppBundleSize = 0;
        public int maxVersionCount = 10;
        public bool buildApp = true;
        public HybridclrAssemblySetting hybridclrAssemblySetting;








        [Essential]
        public HotUpdateBuildSettings buildPathSettings;
        [Essential]
        public FileSystemUtil fileSystemUtil;
        [Essential]
        public ChannelUtil channelUtil;
        [Essential]
        public PlatformPublishUtil publishUtil;
        [Essential]
        public DeviceToolPathSetting deviceToolPaths;




        public (UnityEditor.BuildTargetGroup, UnityEditor.BuildTarget) GetTarget()
        {
            var buildTargetGroup = UnityEditor.BuildTargetGroup.Unknown;
            var buildTarget = UnityEditor.BuildTarget.StandaloneWindows;
            switch (platform)
            {
                case Platform.android:
                    {
                        buildTargetGroup = UnityEditor.BuildTargetGroup.Android;
                        buildTarget = UnityEditor.BuildTarget.Android;
                    }
                    break;
                case Platform.ios:
                    {
                        buildTargetGroup = UnityEditor.BuildTargetGroup.iOS;
                        buildTarget = UnityEditor.BuildTarget.iOS;
                    }
                    break;
                case Platform.WebGL:
                    {
                        buildTargetGroup = UnityEditor.BuildTargetGroup.WebGL;
                        buildTarget = UnityEditor.BuildTarget.WebGL;
                    }
                    break;
                case Platform.OSXUniversal:
                    {
                        buildTargetGroup = UnityEditor.BuildTargetGroup.Standalone;
                        buildTarget = UnityEditor.BuildTarget.StandaloneOSX;
                    }
                    break;
                case Platform.win:
                    {
                        buildTargetGroup = UnityEditor.BuildTargetGroup.Standalone;
                        buildTarget = UnityEditor.BuildTarget.StandaloneWindows;
                    }
                    break;
                case Platform.win64:
                    {
                        buildTargetGroup = UnityEditor.BuildTargetGroup.Standalone;
                        buildTarget = UnityEditor.BuildTarget.StandaloneWindows64;
                    }
                    break;
            }

            return (buildTargetGroup, buildTarget);
        }

        static bool IsPlatformSupportLoaded(BuildTarget target)
        {
            var moduleManager = System.Type.GetType("UnityEditor.Modules.ModuleManager,UnityEditor.dll");

            var ss = moduleManager.GetMethod("GetTargetStringFromBuildTarget", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
            var vv = ss.Invoke(null, new object[] { target });


            var ssss = moduleManager.GetMethod("IsPlatformSupportLoaded", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
            var vv3 = ssss.Invoke(null, new object[] { vv });

            return (bool)vv3;
        }


        static bool IsPlatformSupportIl2cpp(BuildTarget target)
        {
            if(!IsPlatformSupportLoaded(target))
            {
                return false;
            }

            var moduleManager = System.Type.GetType("UnityEditor.Modules.ModuleManager,UnityEditor.dll");

            var ss = moduleManager.GetMethod("GetTargetStringFromBuildTarget", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
            var vv = ss.Invoke(null, new object[] { target });

            var sss = moduleManager.GetMethod("GetScriptingImplementations", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null);
            var vv2 = sss.Invoke(null, new object[] { vv });

            if (vv2 == null)
                return false;
            
            var methods = vv2.GetType().GetMethods();
            var m2 = vv2.GetType().GetMethod("AllowIL2CPPCompilerConfigurationSelection", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            var r2 = m2.Invoke(vv2, null);

            return (bool)r2;
        }

        public bool SwitchPlatform()
        {
            var curTarget = GetTarget();

            UnityEditor.BuildTargetGroup buildTargetGroup = curTarget.Item1;
            UnityEditor.BuildTarget buildTarget = curTarget.Item2;

            if (!IsPlatformSupportLoaded(buildTarget))
            {
                return false;
            }

            return EditorUserBuildSettings.SwitchActiveBuildTarget(buildTargetGroup, buildTarget);
        }

        public void BaseSetPlatformConfig()
        {
            var curTarget = GetTarget();
            UnityEditor.BuildTargetGroup buildTargetGroup = curTarget.Item1;
            UnityEditor.BuildTarget buildTarget = curTarget.Item2;

            bool nativeDebug = developmentBuild;
            {
                {
                    switch (nativeBuildType)
                    {
                        case NativeBuildType.Debug:
                            {
                                EditorUserBuildSettings.iOSXcodeBuildConfig = XcodeBuildConfig.Debug;
                                EditorUserBuildSettings.androidBuildType = AndroidBuildType.Debug;
                            }
                            break;
                        case NativeBuildType.Release:
                            {
                                EditorUserBuildSettings.iOSXcodeBuildConfig = XcodeBuildConfig.Release;
                                EditorUserBuildSettings.androidBuildType = AndroidBuildType.Release;
                            }
                            break;
                    }

                }

                EditorUserBuildSettings.development = developmentBuild;
#if UNITY_2022
            PlayerSettings.SetIl2CppCodeGeneration(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup), il2CppCodeGeneration);
#else
                EditorUserBuildSettings.il2CppCodeGeneration = il2CppCodeGeneration;
#endif
                PlayerSettings.SetScriptingBackend(buildTargetGroup, scriptingImplemention);
                if (platform != Platform.ios)
                {
                    PlayerSettings.SetIl2CppCompilerConfiguration(buildTargetGroup, cppCompiler);
                }

                PlayerSettings.SetApiCompatibilityLevel(buildTargetGroup, apiCompatibilityLevel);

                if (buildTarget == BuildTarget.WebGL)
                {
                    EditorUserBuildSettings.webGLBuildSubtarget = AppManager.GetAppInfo().sub_platform == SubPlatform.wechat ? WebGLTextureSubtarget.ASTC : WebGLTextureSubtarget.DXT;
                }

                PlayerSettings.SetManagedStrippingLevel(buildTargetGroup, managedStrippingLevel);

            }
        }


        void UploadAddressable(string appName, string middleDir, string outputAssetbundleDir)
        {
            string addressableOutputPath = outputAssetbundleDir + $"/{HotUpdateAddressableMgr.hotupdate_dir_name}/{HotUpdateAddressableMgr.hotupdate_dir_name}";
            string addressableInfoPath = addressableOutputPath + "/" + AddressableTool.addressableInfoFileName;
            var infos = JsonConvert.DeserializeObject<AddressableTool.AddressableInfo>(System.IO.File.ReadAllText(addressableInfoPath));
            var addressableVersion = int.Parse(infos.hotupdate_version.ToString());
            var addressableTimestamp = infos.build_timestamp;
            var addreessable_min_app_version = infos.min_app_hotupdate_version;

            string[] filePaths = System.IO.Directory.GetFiles(addressableOutputPath, "*", System.IO.SearchOption.AllDirectories);
            foreach (var p in filePaths)
            {
                fileSystemUtil.Upload(p, $"{appName}/{middleDir}/{HotUpdateAddressableMgr.hotupdate_dir_name}/{AppManager.GetAppInfo().hotupdate_version}_{addressableTimestamp}/" + System.IO.Path.GetFileName(p));
            }





            var md5 = MD5Hash.GetMD5HashFromFile(addressableInfoPath);
            var size = CommonUtil.GetFileSize(addressableInfoPath);

            var newPatchFileName = $"{addressableVersion}_{addreessable_min_app_version}_{addressableTimestamp}_{size}_{md5}";




            List<string> newLines = new List<string>();
            string catalogHashFilePath = System.IO.Directory.GetFiles(addressableOutputPath, "*.hash", System.IO.SearchOption.TopDirectoryOnly)[0];
            string catalogHashFileName = System.IO.Path.GetFileNameWithoutExtension(catalogHashFilePath);
            newLines.Add($"{newPatchFileName} {catalogHashFileName} {enableDownload}");




            var ossVersionPath = $"{appName}/{middleDir}/{HotUpdateAddressableMgr.hotupdate_dir_name}/{HotUpdateAddressableMgr.addressableVersionFileName}";
            string verionsStr = fileSystemUtil.ReadAllText(ossVersionPath);

            if (!string.IsNullOrEmpty(verionsStr))
            {
                string[] lines = verionsStr.Split("\n");

                for (int i = 0; i < lines.Length; i++)
                {
                    var line = lines[i];
                    var words = line.Split("_");
                    if (words.Length < 1)
                    {
                        continue;
                    }
                    var oldVersion = int.Parse(words[0]);
                    if (oldVersion <= addressableVersion)
                    {
                        newLines.Add(line);
                        if (newLines.Count >= maxVersionCount)
                        {
                            break;
                        }
                    }
                }
            }

            verionsStr = string.Join("\n", newLines);
            fileSystemUtil.WriteAllText(ossVersionPath, verionsStr);



        }

        void UploadHotUpdateCode(string appName, string middleDir, string hotupdateCodeBundleDir)
        {
            string hotupdateCodeBundleConfigPath = hotupdateCodeBundleDir + $"/{HotUpdateDllMgr.config_file_name}";

            var infos = JsonConvert.DeserializeObject<HotUpdateCodeInfo>(System.IO.File.ReadAllText(hotupdateCodeBundleConfigPath));
            var hotupdateCodeVersion = int.Parse(infos.hotupdate_version.ToString());
            var hotupdateCodeTimestamp = infos.build_timestamp;
            var hotupdateCode_min_app_version = infos.min_app_hotupdate_version;

            var md5 = MD5Hash.GetMD5HashFromFile(hotupdateCodeBundleConfigPath);
            var srcBundlePath = hotupdateCodeBundleDir + $"/{HotUpdateDllMgr.bundle_name}";
            var size = CommonUtil.GetFileSize(srcBundlePath);

            var newPatchFileName = $"{hotupdateCodeVersion}_{hotupdateCode_min_app_version}_{hotupdateCodeTimestamp}_{size}_{md5}";


            var destBundlePath = hotupdateCodeBundleDir + "/" + newPatchFileName;
            System.IO.File.Copy(srcBundlePath, destBundlePath, true);


            fileSystemUtil.Upload(destBundlePath, $"{appName}/{middleDir}/{HotUpdateDllMgr.hotupdate_code_dir}/" + System.IO.Path.GetFileName(destBundlePath));




            List<string> newLines = new List<string>();
            newLines.Add($"{newPatchFileName} {enableDownload}");





            var ossVersionPath = $"{appName}/{middleDir}/{HotUpdateDllMgr.hotupdate_code_dir}/{HotUpdateDllMgr.dllVersionsFileName}";
            string verionsStr = fileSystemUtil.ReadAllText(ossVersionPath);

            if (!string.IsNullOrEmpty(verionsStr))
            {
                string[] lines = verionsStr.Split("\n");

                for (int i = 0; i < lines.Length; i++)
                {
                    var line = lines[i];
                    var words = line.Split("_");
                    if (words.Length < 1)
                    {
                        continue;
                    }
                    var oldVersion = int.Parse(words[0]);
                    if (oldVersion <= hotupdateCodeVersion)
                    {
                        newLines.Add(line);
                        if (newLines.Count >= maxVersionCount)
                        {
                            break;
                        }
                    }
                }
            }

            verionsStr = string.Join("\n", newLines);
            fileSystemUtil.WriteAllText(ossVersionPath, verionsStr);

        }

        bool CheckoutAssetbundleScm(string middleDir, string outputAssetbundleDir)
        {
            var hotupdateResGitPath = buildPathSettings.hotupdateResGitPath;
            var hotupdateResBuildPath = buildPathSettings.hotupdateResBuildPath;

            if (!string.IsNullOrEmpty(hotupdateResGitPath) && !string.IsNullOrEmpty(hotupdateResBuildPath))
            {
                if (System.IO.Directory.Exists(outputAssetbundleDir))
                {
                    if (!GitTool.IsGitRespository(outputAssetbundleDir))
                    {
                        string[] filePaths = System.IO.Directory.GetFiles(outputAssetbundleDir, "*", System.IO.SearchOption.AllDirectories);
                        if (filePaths.Length <= 0)
                        {
                            System.IO.Directory.Delete(outputAssetbundleDir, true);
                        }
                        else
                        {
                            return false;
                        }
                    }
                }

                var hotupdateResOriginUrl = GitTool.GetConfigByKey(outputAssetbundleDir, "remote.origin.url");

                if (hotupdateResOriginUrl != hotupdateResGitPath)
                {
                    if (string.IsNullOrEmpty(hotupdateResOriginUrl))
                    {
                        if (System.IO.Directory.Exists(outputAssetbundleDir) || System.IO.File.Exists(outputAssetbundleDir))
                        {
                            Debug.LogError($"path {outputAssetbundleDir} existed ");
                            return false;
                        }

                        GitTool.Clone(hotupdateResGitPath, outputAssetbundleDir);
                    }
                    else
                    {
                        Debug.LogError("hotupdateResOriginUrl isn't " + hotupdateResGitPath);
                        return false;
                    }
                }



                {
                    if (!GitTool.Restore(outputAssetbundleDir, "."))
                    {
                        Debug.LogError("git restore failed: " + outputAssetbundleDir);
                        return false;
                    }

                    if (!GitTool.Clean(outputAssetbundleDir))
                    {
                        Debug.LogError("git clean failed: " + outputAssetbundleDir);
                        return false;
                    }

                    if (!GitTool.CheckoutBranch(outputAssetbundleDir, middleDir))
                    {
                        if (!GitTool.CreateOrphanBranch(outputAssetbundleDir, middleDir))
                        {
                            Debug.LogError("git CreateNewBranch failed: " + outputAssetbundleDir + " | " + middleDir);
                            return false;
                        }
                    }
                }
            }

            return true;
        }

        bool CommitAssetbundleScm(string outputAssetbundleDir)
        {
            var hotupdateResGitPath = buildPathSettings.hotupdateResGitPath;
            var hotupdateResBuildPath = buildPathSettings.hotupdateResBuildPath;

            if (!string.IsNullOrEmpty(hotupdateResGitPath) && !string.IsNullOrEmpty(hotupdateResBuildPath))
            {
                var hotupdateResOriginUrl = GitTool.GetConfigByKey(outputAssetbundleDir, "remote.origin.url");

                if (hotupdateResOriginUrl == hotupdateResGitPath)
                {
                    if (!GitTool.Add(outputAssetbundleDir, "."))
                    {
                        Debug.LogError("git Add failed: " + outputAssetbundleDir);
                        return false;
                    }

                    if (!GitTool.Commit(outputAssetbundleDir, "abcde"))
                    {
                        Debug.LogError("git Commit failed: " + outputAssetbundleDir);
                        return false;
                    }

                    if (!GitTool.Push(outputAssetbundleDir))
                    {
                        Debug.LogError("git Push failed: " + outputAssetbundleDir);
                        return false;
                    }
                }
            }
            return true;
        }

        [MenuItem("Assets/cframework/BuildPlayer/CheckEssential")]
        public static void CheckEssentialFromMenu()
        {
            var bps = Selection.activeObject as BuildPlayerSetting;
            if (bps == null)
            {
                return;
            }

            bps.CheckEssential();
        }

        [MenuItem("Assets/cframework/BuildPlayer/ShowAllToolPaths")]
        public static void ShowAllToolPathsFromMenu()
        {
            var bps = Selection.activeObject as BuildPlayerSetting;
            if (bps == null)
            {
                return;
            }

            bps.ShowAllToolPaths();
        }


        [MenuItem("Assets/cframework/BuildPlayer/Build")]
        static void BuildMenuItem()
        {
            var bps = Selection.activeObject as BuildPlayerSetting;
            if (bps != null)
            {
                bps.Build();
            }
        }

        [MenuItem("Assets/cframework/BuildPlayer/SwitchPlatform")]
        static void SwitchPlatformMenuItem()
        {
            var bps = Selection.activeObject as BuildPlayerSetting;
            if (bps != null)
            {
                bps.SwitchPlatform();
            }
        }


        [ContextMenu("CheckEssential")]
        bool CheckEssential()
        {
            var checkResult = EssentialToolsAttribute.Check(this, this.deviceToolPaths);


            if (!string.IsNullOrEmpty(checkResult.Item1))
            {
                if (!string.IsNullOrEmpty(checkResult.Item2))
                {
                    EditorUtility.DisplayDialog("tool not exists: ", checkResult.Item1 + ": " + checkResult.Item2, "ok");
                }
                else
                {
                    if(string.IsNullOrEmpty(checkResult.Item3))
                    { 
                        EditorUtility.DisplayDialog("tool not found: ", checkResult.Item1, "cancel build");
                    }
                    else
                    {
                        if(EditorUtility.DisplayDialog("tool not found: ", checkResult.Item1 + "\n" + checkResult.Item3, "download", "cancel build"))
                        {
                            Application.OpenURL(checkResult.Item3);
                        }
                    }
                }
                return false;
            }

            var fieldNames = EssentialAttribute.Check(this, this.name);
            if (fieldNames.Count() > 0)
            {
                return EditorUtility.DisplayDialog("field not set: ", string.Join("\n", fieldNames), "continue build?", "cancel");
            }
            return true;
        }


        [ContextMenu("ShowAllToolPaths")]
        void ShowAllToolPaths()
        {
            var results = new List<(string, string, string)>();
            EssentialToolsAttribute.GetAllToolPaths(this, this.deviceToolPaths, null, results);

            string content = "";
            for(int i = 0; i < results.Count; i++)
            {
                content += results[i].Item1 + "\n" + results[i].Item2 + "\n" + results[i].Item3 + "\n\n";
            }

            EditorUtility.DisplayDialog("all tool paths", content, "ok");
        }

        public virtual bool SetPlatformConfig()
        {
            return true;
        }


        public PublishInfo GetPublishInfo()
        {
            PublishInfo publishInfo = new PublishInfo();
            #region set local var
            
            string projectPath = System.IO.Path.GetDirectoryName(Application.dataPath).Replace("\\", "/");

            

            string branchName = GitTool.GetBranchName(projectPath);
            string lastChangedVersion = GitTool.GetProjectLastChangedVersion(projectPath);
            int hotupdateVersionNumber = GitTool.GetVersionNumber(projectPath);


            string middleDir = branchName + "_" + platform + "_" + subPlatform + "_" + publishType;
            var frameworkDataPath = buildPathSettings.frameworkDataPath;
            string outputAssetbundleDir = System.IO.Path.GetFullPath(buildPathSettings.hotupdateResBuildPath, projectPath).Replace("\\", "/");
            outputAssetbundleDir += "/" + this.platform.ToString() + "_" + this.publishType;
            string buildPath = projectPath + "/" + buildPathSettings.buildPath;
            string outputProjectPath = buildPath + "/project";
            string buildOutputPath = buildPath + "/output";


            System.DateTime now = System.DateTime.Now;
            System.DateTime epochStart = new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
            ulong timestamp = (ulong)(now.Subtract(epochStart).TotalSeconds);



            string realAppVersion = appVersion;
            if (string.IsNullOrEmpty(appVersion))
            {
                realAppVersion = appVersionNumber.ToString();
            }

            if (minAppVersion != 0)
            {
                minAppVersion = Mathf.Min(minAppVersion, hotupdateVersionNumber);
            }

            string appName = this.buildPathSettings.appName;
            if (string.IsNullOrEmpty(appName))
            {
                appName = Application.productName;

                if (string.IsNullOrEmpty(appName))
                {
                    EditorUtility.DisplayDialog($"set app name first", $"", "ok");
                    return null;
                }
            }

            publishInfo.projectPath = projectPath;
            publishInfo.frameworkDataPath = frameworkDataPath;
            publishInfo.outputAssetbundleDir = outputAssetbundleDir;
            publishInfo.buildPath = buildPath;
            publishInfo.outputProjectPath = outputProjectPath;
            publishInfo.buildOutputPath = buildOutputPath;
            publishInfo.appName = appName;
            publishInfo.middleDir = middleDir;
            publishInfo.hotupdateVersionNumber = hotupdateVersionNumber;
            publishInfo.timestamp = timestamp;
            publishInfo.branchName = branchName;
            publishInfo.lastChangedVersion = lastChangedVersion.ToString();
            publishInfo.realAppVersion = realAppVersion;

            #endregion

            return publishInfo;
        }
            
        public void SetHotUpdateAppInfo(PublishInfo publishInfo)
        {
            {
                string web_host = fileSystemUtil.GetDownloadUrl() + "/" + publishInfo.appName + "/" + publishInfo.branchName + "_" + platform + "_" + subPlatform + "_" + publishType;


                string infoFilePath = publishInfo.frameworkDataPath + "/Game/Resources/" + AppManager.appInfoFileName;
                string str = System.IO.File.ReadAllText(infoFilePath);
                var appInfo = JsonConvert.DeserializeObject<HotUpdateAppInfo>(str);
                appInfo.app_name = publishInfo.appName;
                appInfo.hotupdate_url = web_host;

                appInfo.enable_hotupdate = enableHotUpdate;

                appInfo.hotupdate_version = publishInfo.hotupdateVersionNumber;
                appInfo.publish_type = publishType;
                appInfo.sub_platform = subPlatform;

                appInfo.branch_name = publishInfo.branchName;
                appInfo.repository_version = publishInfo.lastChangedVersion;

                appInfo.datetime = DateTime.Now.ToString();
                appInfo.timestamp = publishInfo.timestamp;

                str = JsonConvert.SerializeObject(appInfo, Formatting.Indented);
                System.IO.File.WriteAllText(infoFilePath, str);

                AppManager.Reset();
            }

            AssetDatabase.Refresh();
        }

        public bool CheckAndCreateDir(PublishInfo publishInfo)
        {

            if (GitTool.HasChanges(publishInfo.projectPath))
            {
                if (!EditorUtility.DisplayDialog("warning", "has changes, continue build?", "continue", "cancel"))
                {
                    return false;
                }
            }

            if (System.IO.Directory.Exists(publishInfo.buildPath) && System.IO.Directory.GetFiles(publishInfo.buildPath, "*", System.IO.SearchOption.AllDirectories).Length > 0)
            {
                if (EditorUtility.DisplayDialog($"{publishInfo.buildPath} existed", $"{publishInfo.buildPath} existed", "delete", "cancel"))
                {
                    System.IO.Directory.Delete(publishInfo.buildPath, true);
                }
                else
                {
                    return false;
                }
            }



            if (!System.IO.Directory.Exists(publishInfo.buildPath))
            {
                System.IO.Directory.CreateDirectory(publishInfo.buildPath);
            }

            if (!System.IO.Directory.Exists(publishInfo.buildOutputPath))
            {
                System.IO.Directory.CreateDirectory(publishInfo.buildOutputPath);
            }
            return true;
        }

        public static bool OpenHybridCLRInstallWindow()
        {
            var ass = System.AppDomain.CurrentDomain.GetAssemblies().First((a) => { return a.GetName().Name == "HybridCLR.Editor"; });

            var t = ass.GetType("HybridCLR.Editor.Settings.MenuProvider");
            if (t == null)
            {
                Debug.LogError("type not found: " + "WeChatWASMHybridCLR.Editor.Settings.MenuProvider");
                return false;
            }

            var m = t.GetMethod("Open", BindingFlags.NonPublic | BindingFlags.Static);
            var a = m.Invoke(null, null);


            return true;
        }


        public static bool InstallHybridCLR()
        {
            OpenHybridCLRInstallWindow();

            var win = EditorWindow.GetWindow(typeof(HybridCLR.Editor.Installer.InstallerWindow));
            var methodInfo = win.GetType().GetMethod("InstallLocalHybridCLR", BindingFlags.NonPublic | BindingFlags.Instance);
            methodInfo.Invoke(win, null);

            return true;
        }

        public void Build()
        {

            #region check hybridclr
            var hybridclrInstaller = new HybridCLR.Editor.Installer.InstallerController();
            if (!hybridclrInstaller.HasInstalledHybridCLR())
            {
                int option = EditorUtility.DisplayDialogComplex("install hybridclr first", "install hybridclr first", "install", "cancel", "open install window");
                switch(option)
                {
                    case 0: InstallHybridCLR(); break;
                    case 2: OpenHybridCLRInstallWindow(); break;
                    case 1: return;
                }
            }
            #endregion




            if (!CheckEssential())
            {
                return;
            }

            GitTool.GitPath = deviceToolPaths.GetCmdToolPath("git");

            string projectPath = System.IO.Path.GetDirectoryName(Application.dataPath).Replace("\\", "/");

            if (!GitTool.IsGitRespository(projectPath))
            {
                EditorUtility.DisplayDialog("not a git project", projectPath, "ok");
                return;
            }



            #region check platform
            var (buildTargetGroup, buildTarget) = GetTarget();


            if (!IsPlatformSupportLoaded(buildTarget))
            {
                if (EditorUtility.DisplayDialog("cannot load target: " + buildTarget, "install platform first: "  + buildTarget, "ok"))
                {
                }
                return;
            }

            if(this.scriptingImplemention == ScriptingImplementation.IL2CPP)
            {
                if(!IsPlatformSupportIl2cpp(buildTarget))
                {
                    if (EditorUtility.DisplayDialog("not support il2cpp: " + buildTarget, "install il2cpp module first: " + buildTarget, "ok"))
                    {
                    }
                    return;
                }
            }

            if (EditorUserBuildSettings.activeBuildTarget != buildTarget)
            {
                if (EditorUtility.DisplayDialog("switch platform first", "switch platform first", "switch", "cancel"))
                {
                    SwitchPlatform();
                }
                return;
            }


            

            SwitchPlatform();
            #endregion

            InitFrameworkTool.InitFramework();

            BaseSetPlatformConfig();
            if (!SetPlatformConfig())
            {
                return;
            }


            

            PublishInfo publishInfo = GetPublishInfo();
            if(publishInfo == null)
            {
                return;
            }

            if(!CheckAndCreateDir(publishInfo))
            {
                return;
            }

            SetHotUpdateAppInfo(publishInfo);








            VersionTool.SetVersion(appVersion, appVersionNumber);
            PlayerSettings.SetApplicationIdentifier(buildTargetGroup, packageName);

            AssetDatabase.SaveAssets();


            if (enableHotUpdate)
            {
                if (!CheckoutAssetbundleScm(publishInfo.middleDir, publishInfo.outputAssetbundleDir))
                {
                    return;
                }
            }

            #region change first scene
            ChangeFirstScene(buildPathSettings.frameworkDataPath + $"/HotUpdate/{LoadDllConfig.fileName}");
            #endregion


            #region build addressable
            AddressableTool.Build(enableHotUpdate, minAppVersion, enableHotUpdate ? publishInfo.outputAssetbundleDir + $"/{HotUpdateAddressableMgr.hotupdate_dir_name}" : null);
            if (enableHotUpdate)
            {
                AddressableTool.CopyLocalBundleToStreamingAssetDir(inAppBundleSize);
            }
            #endregion

            //if(buildApp)
            //{
            //    if(platform != Platform.WebGL || subPlatform != SubPlatform.wechat)
            //    {
            //        BuildTool.Build(outputProjectPath);
            //    }
            //}

            #region hybridclr setting
            if (this.hybridclrAssemblySetting != null)
            {
                this.hybridclrAssemblySetting.Set(this.enableHotUpdate);
            }
            else
            {
                HybridCLR.Editor.SettingsUtil.HybridCLRSettings.enable = this.enableHotUpdate;
                HybridCLR.Editor.Settings.HybridCLRSettings.Save();
            }
            #endregion

            #region link.xml for hybridclr
            {
                string hotupdateLinkXmlFilePath = publishInfo.frameworkDataPath + "/hotupdate_link.xml";
                string newFilePath = System.IO.Path.GetDirectoryName(hotupdateLinkXmlFilePath) + "/link.xml";
                if (enableHotUpdate)
                {
                    if (System.IO.File.Exists(hotupdateLinkXmlFilePath))
                    {
                        System.IO.File.Copy(hotupdateLinkXmlFilePath, newFilePath, true);
                    }
                }
                else
                {
                    if (System.IO.File.Exists(newFilePath))
                    {
                        System.IO.File.Delete(newFilePath);
                    }
                }
            }
            #endregion


            if (enableHotUpdate)
            {
                #region build hybridclr
                {
                    PrebuildCommand.GenerateAll();
                    HotUpdateCodeTool.BuildHotUpdateDll(publishInfo.outputAssetbundleDir + $"/{HotUpdateDllMgr.hotupdate_code_dir}", minAppVersion, null);
                    HotUpdateCodeTool.BuildHotUpdateBundle(buildPathSettings.frameworkDataPath + "/hotupdate_res.txt", publishInfo.outputAssetbundleDir + $"/{HotUpdateDllMgr.hotupdate_code_dir}/bundle", publishInfo.outputAssetbundleDir + $"/{HotUpdateDllMgr.hotupdate_code_dir}/{HotUpdateDllMgr.hotupdate_code_dir}", null);

                    CommonUtil.CopyDirectory(publishInfo.outputAssetbundleDir + $"/{HotUpdateDllMgr.hotupdate_code_dir}/bundle", $"Assets/StreamingAssets/{HotUpdateDllMgr.hotupdate_code_dir}");

                    //if (buildApp)
                    //{
                    //    HotUpdateCodeTool.CopyHotUpdateDllBundleToPlayer(outputAssetbundleDir + $"/{HotUpdateDllMgr.hotupdate_code_dir}/bundle", outputProjectPath);
                    //}

                    AssetDatabase.Refresh();
                }
                #endregion

                #region upload hybridclr and addressable
                {
                    UploadAddressable(publishInfo.appName, publishInfo.middleDir, publishInfo.outputAssetbundleDir);
                    UploadHotUpdateCode(publishInfo.appName, publishInfo.middleDir, publishInfo.outputAssetbundleDir + $"/{HotUpdateDllMgr.hotupdate_code_dir}/bundle");
                }
                #endregion
            }

            #region build app
            if (buildApp)
            {
                if (platform != Platform.WebGL || subPlatform != SubPlatform.wechat)
                {
                    BuildTool.Build(publishInfo.outputProjectPath);
                }
            }
            #endregion



            #region publish
            if (buildApp)
            {
                publishUtil?.BuildAndPublish(this, publishInfo);
            }
            else
            {
                StartHttpServer();
            }
            #endregion

            if (enableHotUpdate)
            {
                if (!CommitAssetbundleScm(publishInfo.outputAssetbundleDir))
                {
                    if (!EditorUtility.DisplayDialog("commit asset bundle scm failed", "conintue build?", "continue", "cancel"))
                    {
                        return;
                    }
                }
            }
        }


        public static void ChangeFirstScene(string gameSceneConfigPath)
        {
            var a = gameSceneConfigPath;
            if (!System.IO.File.Exists(a))
            {
                return;
            }

            string dir = System.IO.Path.GetDirectoryName(gameSceneConfigPath);

            string c = System.IO.File.ReadAllText(a);
            var d = JsonConvert.DeserializeObject<LoadDllConfig>(c);

            {
                var scenes = EditorBuildSettings.scenes;
                foreach(var s in scenes)
                {
                    if(s.path == d.launchHotUpdateScene)
                    {
                        s.enabled = AppManager.GetAppInfo().enable_hotupdate;
                        AddressableTool.DisableAssetAddressable(s.path);
                    }

                    if (s.path == d.gameScene)
                    {
                        s.enabled = !AppManager.GetAppInfo().enable_hotupdate;
                        if(s.enabled)
                        {
                            AddressableTool.DisableAssetAddressable(s.path);
                        }
                    }
                }


                int launchSceneIndex = System.Array.FindIndex(scenes, (a) => a.path == d.launchHotUpdateScene);
                var launchScene = scenes[launchSceneIndex];
                for(int i = launchSceneIndex; i > 0; i--)
                {
                    scenes[i] = scenes[i - 1];
                }
                scenes[0] = launchScene;

                //var key = AppManager.GetAppInfo().enable_hotupdate ? d.launchHotUpdateScene : d.gameScene;

                //var b = new string[] { key };

                //EditorBuildSettingsScene[] scenes = new EditorBuildSettingsScene[b.Length];
                //for (int i = 0; i < b.Length; i++)
                //{
                //    scenes[i] = new EditorBuildSettingsScene(b[i], true);
                //    AddressableTool.DisableAssetAddressable(b[i]);
                //}

                EditorBuildSettings.scenes = scenes;

                AssetDatabase.SaveAssets();
            }

            if (AppManager.GetAppInfo().enable_hotupdate)
            {
                var key = d.gameScene;

                var b = new string[] { key };

                for (int i = 0; i < b.Length; i++)
                {
                    AddressableTool.EnableAssetAddressable(b[i]);
                }

                AssetDatabase.SaveAssets();
            }

            //string dir = System.IO.Path.GetDirectoryName(gameSceneConfigPath);

            //string c = System.IO.File.ReadAllText(a);
            //var d = JsonConvert.DeserializeObject<Dictionary<string, string[]>>(c);

            //{
            //    var key = AppManager.GetAppInfo().enable_hotupdate? "hotupdate_enabled": "hotupdate_disabled";

            //    var b = d[key];

            //    EditorBuildSettingsScene[] scenes = new EditorBuildSettingsScene[b.Length];
            //    for(int i = 0; i < b.Length; i++)
            //    {
            //        scenes[i] = new EditorBuildSettingsScene(dir + "/" + b[i], true);
            //        AddressableTool.DisableAssetAddressable(dir + "/" + b[i]);
            //    }

            //    EditorBuildSettings.scenes = scenes;

            //    AssetDatabase.SaveAssets();
            //}

            //if(AppManager.GetAppInfo().enable_hotupdate)
            //{
            //    var key = "hotupdate_disabled";

            //    var b = d[key];

            //    for(int i = 0; i < b.Length; i++)
            //    {
            //        AddressableTool.EnableAssetAddressable(b[i]);
            //    }


            //    AssetDatabase.SaveAssets();
            //}
        }

        public void StartHttpServer()
        {
            if (this.fileSystemUtil is FileSystemUtil_LocalHttp)
            {
                var localHttp = this.fileSystemUtil as FileSystemUtil_LocalHttp;
                var option = EditorUtility.DisplayDialogComplex("start http server?", "start http server?", "start server", "start python server", "cancel");
                switch (option)
                {
                    case 0: localHttp.StartHttpServer(); break;
                    case 1: localHttp.StartPythonHttpServer(); break;
                }
            }
        }


        [MenuItem("Assets/cframework/SCM/RestoreAndCleanAllChanges")]
        [MenuItem("cframework/SCM/RestoreAndCleanAllChanges")]
        public static void RestoreAndCleanAllChanges()
        {
            if (EditorUtility.DisplayDialog("restore and clean all changes?", "restore and clean all changes?", "yes", "no"))
            {
                GitTool.Restore(System.IO.Path.GetDirectoryName(Application.dataPath), ".");
                GitTool.Clean(System.IO.Path.GetDirectoryName(Application.dataPath));

                AssetDatabase.Refresh();
            }
        }
    }
}